#include "bootota.h"

#include <QMessageBox>
#include <QDebug>


BootOTA::BootOTA(Ui::MainWindow * pui,QWidget *parent)
    : QMainWindow{parent}
{
    ui = pui;
    m_OTAConnectCnt = 0;
    connect(ui->BtnSerialCtl, &QPushButton::clicked, this, &BootOTA::onBtnSerialCtlClicked);
    connect(ui->BtnOTAConnect, &QPushButton::clicked, this, &BootOTA::onBtnOTAConnectClicked);
    connect(ui->BtnOTAUpdate, &QPushButton::clicked, this, &BootOTA::onBtnOTAUpdateClicked);
    connect(ui->BtnSerialFlush, &QPushButton::clicked, this, &BootOTA::onBtnSerialFlushClicked);
    connect(ui->BtnOTAExdMsgClear, &QPushButton::clicked, this, &BootOTA::onBtnOTAExdMsgClearClicked);

    serialInit();
    // 串口接收信号
    connect(&this->m_serial, &QSerialPort::readyRead, this, &BootOTA::onSerialReceived);
    initCRC32Table();

    serialRecvTimInit();

    // 更新线程
    m_OTAUpdateThread = new OTAUpdateThread(this);
    connect(this, &BootOTA::OTADataReady, m_OTAUpdateThread, &OTAUpdateThread::onOTADataReadySlot);
    connect(m_OTAUpdateThread, &OTAUpdateThread::OTASerialSendData, this, &BootOTA::onOTASerialSendDataSlot);
    connect(m_OTAUpdateThread, &OTAUpdateThread::OTALog, this, &BootOTA::onOTALogSlot);
    connect(m_OTAUpdateThread, &OTAUpdateThread::OTASetBlock, this, &BootOTA::onOTASetBlockSlot);
    connect(m_OTAUpdateThread, &OTAUpdateThread::OTASetAllBlock, this, &BootOTA::onOTASetAllBlockSlot);
    connect(this, &BootOTA::OTAUpdateStart, m_OTAUpdateThread, &OTAUpdateThread::onOTAUpdateStartSlot);
    connect(this, &BootOTA::OTAUpdateStop, m_OTAUpdateThread, &OTAUpdateThread::onOTAUpdateStopSlot);
}

BootOTA::~BootOTA()
{
    delete m_serialRecvTim;

    //QThread线程安全释放顺序
    delete m_OTAUpdateThread;
}

void BootOTA::serialInit()
{
    //读取串口信息
    foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts())
    {
        // 自动读取串口号添加到端口portBox中
        ui->ComBoxSerialPort->addItem(info.portName());
    }

    QStringList baudList;   //波特率
    // 波特率    //波特率默认选择下拉第三项：9600
    baudList<<"110"<<"1200"<<"2400"<<"4800"<<"9600"
             <<"14400"<<"19200"<<"38400"<<"56000"
             <<"57600"<<"115200";
    ui->ComBoxSerialBuad->addItems(baudList);
    ui->ComBoxSerialBuad->setCurrentIndex(10);

    ui->BtnOTAConnect->setEnabled(false);
    ui->BtnOTAUpdate->setEnabled(false);
}


void BootOTA::serialRecvTimInit()
{
    m_serialRecvTim = new QTimer();
    m_serialRecvTim->setInterval(1000); // 1s

    connect(m_serialRecvTim, &QTimer::timeout, this, &BootOTA::onSerialRecvTimTimeoutSlot);
}

void BootOTA::serialRecvTimStart()
{
    m_serialRecvTim->start();
}

void BootOTA::serialRecvTimStop()
{
    m_serialRecvTim->stop();
}


void BootOTA::OTASerialPortSend(QByteArray& byteArr)
{
    if(m_serial.isOpen())
    {
        sumCheck(byteArr);
        CRC32(byteArr);
        m_serial.write(byteArr);
        m_serial.flush();
        // qDebug() << "write byteArr:" << byteArr.toHex();
        QString str = QString("发：%1\r\n").arg(byteArr.toHex(' ').toUpper());
        ui->TxtEdUartMsg->append(str);

        if(!m_serial.waitForBytesWritten(300)){
            qDebug()<<"SerialPort write error"<<m_serial.errorString();
            m_serial.clearError();
        }else
        {
            // qDebug()<< "SerialPort write ok";
        }

    }else
    {
        QMessageBox::critical(NULL, "错误", "串口未打开");
    }
}

void BootOTA::OTAConnectHandler(QByteArray &data)
{
    if(data.at(1) == m_OTAConnectCnt && !data.at(2))
    {
        ui->TxtEdUartMsg->append("连接成功！");
        ui->BtnOTAUpdate->setEnabled(true);
    }
    else
    {
        QString str = QString("连接失败： %1").arg(data.at(2));
        ui->TxtEdUartMsg->append(str);
    }
}

void BootOTA::OTASerialPortRecvParse(QByteArray& byteRecv)
{
    if(byteRecv.size() < OTA_PACK_LEN)
    {
        ui->TxtEdUartMsg->append("接收数据size太小!");
        return;
    }
    QByteArray clentid;
    clentid.resize(2);
    clentid[0] = (BOOT_ID >> 8)&0xff;
    clentid[1] = BOOT_ID&0xff;

    if(!byteRecv.startsWith(clentid)) // 开头
    {
        // ui->TxtEdUartMsg->append("单片机返回ID不正确！");
        return;
    }
    QString str = QString("收：%1\r\n").arg(byteRecv.toHex(' ').toUpper());
    ui->TxtEdUartMsg->append(str);

    // 检查校验
    QByteArray byteCheck = byteRecv;
    sumCheck(byteCheck);
    CRC32(byteCheck);
    byteCheck.remove(0,11);
    // qDebug() << byteCheck.toHex(' ').toUpper();
    if(!byteRecv.endsWith(byteCheck))
    {
        ui->TxtEdUartMsg->append("单片机返回 数据校验不正确！");
        return;
    }
    // 分离数据
    quint8 len = byteRecv.at(2);

    if(len > 0)
    {
        m_SerialDataRDY.clear();
        m_SerialDataRDY = byteRecv.mid(3,len);
        // 产生信号接收
        OTAMessageSortProcess(m_SerialDataRDY);
    }
    else
    {
        ui->TxtEdUartMsg->append("");
    }
}

void BootOTA::OTAMessageSortProcess(QByteArray &data)
{
    switch((std::uint8_t)data.at(0))
    {
    case OTA::FUNC_CONNECT:
        OTAConnectHandler(data);
        break;
    default:
        // 不是连接的都发射信号，在线程里面处理
        emit OTADataReady(data);
        break;
    }
}

void BootOTA::onOTASerialSendDataSlot(QByteArray data)
{
    OTASerialPortSend(data);
}

void BootOTA::onOTALogSlot(QString string)
{
    ui->TxtEdUartMsg->append(string);
}

void BootOTA::onOTASetBlockSlot(qsizetype block, qsizetype fsn)
{
    ui->LabBlock->setText(QString::number(block));
    ui->LabFSN->setText(QString::number(fsn));
}

void BootOTA::onOTASetAllBlockSlot(qsizetype block)
{
    ui->LabAll->setText(QString::number(block));
}

void BootOTA::onBtnSerialFlushClicked()
{
    ui->ComBoxSerialPort->clear();
    //通过QSerialPortInfo查找可用串口
    foreach(const QSerialPortInfo &info,QSerialPortInfo::availablePorts())
    {   //在portBox中显示可用串口
        QString str =  QString("%1 %2").arg(info.portName()).arg(info.description());
        ui->ComBoxSerialPort->addItem(str);
    }

    int max_len=0;
    for(int idx=0;idx < ui->ComBoxSerialPort->count();idx++)
    {
        if(max_len < ui->ComBoxSerialPort->itemText(idx).length())
            max_len = ui->ComBoxSerialPort->itemText(idx).length();
    }

    int pt_val = ui->ComBoxSerialPort->font().pointSize();//获取字体的磅值
    ui->ComBoxSerialPort->setFixedWidth(max_len*pt_val*0.75);//（字符数*每个字符的宽度(磅)*0.75）个像素
}

void BootOTA::onBtnSerialCtlClicked()
{
    // 打开串口
    if(ui->BtnSerialCtl->text() == "打开串口")
    {
        // 设置串口号
        QStringList strList = ui->ComBoxSerialPort->currentText().split(" ");
        m_serial.setPortName(strList[0]);
        // 打开串口
        if(m_serial.open(QIODevice::ReadWrite))
        {
            // 设置波特率
            m_serial.setBaudRate(ui->ComBoxSerialBuad->currentText().toInt());

            //设置数据位数

            // 设置校验位
            //SerialPort->setParity(QSerialPort::NoParity);
            //设置奇偶校验
            m_serial.setParity(QSerialPort::NoParity); // 无校验

            // 设置流控制
            m_serial.setFlowControl(QSerialPort::NoFlowControl);
            //设置停止位
            m_serial.setStopBits(QSerialPort::OneStop);
        }
        //打开串口
        else
        {
            QMessageBox::about(NULL, "提示", "串口无法打开\r\n不存在或已被占用");
            return;
        }
        ui->BtnSerialCtl->setText("关闭串口");
        //下拉菜单控件使能
        ui->ComBoxSerialPort->setEnabled(false);
        ui->ComBoxSerialBuad->setEnabled(false);
        //搜索串口按键使能
        ui->BtnSerialFlush->setEnabled(false);
        //发送按键使能
        ui->BtnOTAConnect->setEnabled(true);
    }
    // 关闭串口
    else
    {
        m_serial.close();
        ui->BtnSerialCtl->setText("打开串口");
        ui->BtnOTAUpdate->setText("开始更新");
        //下拉按键使能
        ui->ComBoxSerialPort->setEnabled(true);
        ui->ComBoxSerialBuad->setEnabled(true);
        //搜索串口按键使能
        ui->BtnSerialFlush->setEnabled(true);
        //发送按键使能
        ui->BtnOTAConnect->setEnabled(false);
        ui->BtnOTAUpdate->setEnabled(false);
    }
}

void BootOTA::onBtnOTAConnectClicked()
{
    QByteArray connectArr(OTA_PACK_LEN, 0);
    createHostPackage(connectArr);
    connectArr[2] = 0x04; // 长度
    connectArr[3] = OTA::FUNC_CONNECT;  // 功能码
    connectArr[4] = ++m_OTAConnectCnt;
    connectArr[5] = 0x00;
    connectArr[6] = 0x01;
    // 发送
    OTASerialPortSend(connectArr);
}

void BootOTA::onBtnOTAExdMsgClearClicked()
{
    ui->TxtEdUartMsg->clear();
}

void BootOTA::onSerialReceived()
{
    QByteArray readbuff = m_serial.readAll();
    OTASerialPortRecvParse(readbuff); // 解析
}

void BootOTA::onSerialRecvTimTimeoutSlot()
{

}

void BootOTA::onBtnOTAUpdateClicked()
{
    // 开始更新
    if(ui->BtnOTAUpdate->text() == "开始更新")
    {
        ui->BtnOTAUpdate->setText("停止更新");
        emit OTAUpdateStart();
    }
    else // 停止更新
    {
        ui->BtnOTAUpdate->setText("开始更新");
        emit OTAUpdateStop();
    }
}

